﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data.Entity.SqlServer;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Model;
using Hyt.Core;
using Hyt.Core.Cache;

namespace Hyt.Data.Ef
{
    public class HytDbContext : System.Data.Entity.DbContext
    {

        
        #region 辅助类
        protected sealed class HytDbMigrationsConfiguration<T> : DbMigrationsConfiguration<T> where T : System.Data.Entity.DbContext
        {
            public HytDbMigrationsConfiguration()
            {
                AutomaticMigrationsEnabled = true;
                AutomaticMigrationDataLossAllowed = true;
                this.SetSqlGenerator("System.Data.SqlClient", new NoForeignKeySqlServerMigrationSqlGenerator());
            }

        }

        internal sealed class NoForeignKeySqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator 
        {
            protected override void Generate(AddForeignKeyOperation addForeignKeyOperation)
            {
                // base.Generate(addForeignKeyOperation);
            }
            protected override void Generate(CreateTableOperation createTableOperation)
            {
                //base.Generate(createTableOperation);
            }
            protected override void Generate(AddColumnOperation addColumnOperation)
            {
                //base.Generate(addColumnOperation);
            }
            protected override void Generate(CreateIndexOperation createIndexOperation)
            {
                //base.Generate(createIndexOperation);
            }
             
        }
        #endregion

        #region 构造方法
        /// <summary>
        /// 初始化
        /// </summary>
        public HytDbContext()
            : base()
        {

        }
        /// <summary>
        /// 初始化
        /// </summary>
        public HytDbContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
          
        }
        #endregion

        #region 读写分离方法

        enum SyncStatus { UNKOWN, UNNEEDSYNC, NEEDSYNC }

        public CacheManager CacheManager { get; set; }

        public string SessionId { get; set; }

        /// <summary>
        /// 通知数据更改
        /// </summary>
        protected virtual void NotifyChanged()
        {
            CacheManager.Set("DBLASTMODIFY_" + SessionId, DateTime.Now);
        }

        /// <summary>
        /// 获取从库数据最后更新时间
        /// </summary>
        /// <returns></returns>
        protected virtual DateTime GetLastModify()
        {
            var date = ExecuteSqlQuery<DateTime>("select top 1  modifydate from dbo.Sync where sessionid='" + "DBLASTMODIFY_" + SessionId + "' order by id desc");
            if (date.MoveNext())
            {
                return date.Current;
            }
            return DateTime.Now;
        }

        /// <summary>
        /// 主从是否同步
        /// </summary>
        /// <returns></returns>
        protected virtual bool IsSynced()
        {
            return true;
        }
        #endregion

        public IQueryable<T> GetQuery<T>(QueryOption<T> option = null) where T : class
        {
            var query = this.Set<T>().AsQueryable();
            if (option != null)
            {
                if (option.Includes != null)
                {
                    foreach (var include in option.Includes)
                    {
                        query = query.Include(include);
                    }
                }
                if (option.NoTracking)
                {
                    query = query.AsNoTracking();
                }
                if (option.Parallel)
                {
                    query = query.AsParallel().AsQueryable();
                }
            }
            return query;
        }

        public T Get<T>(params object[] keys) where T : class
        {
            return this.Set<T>().Find(keys);
        }
       
        public void Remove<T>(T model) where T : class
        {
            var state = GetStatus(model);
            if (state == Hyt.Core.EntityState.Detached)
            {
                this.Set<T>().Attach(model);
            }
            this.Set<T>().Remove(model);
        }

        public T Add<T>(T model) where T : class
        {
            var state = GetStatus(model);
            if ((state | Hyt.Core.EntityState.Added) == Hyt.Core.EntityState.Added)
            {
                throw new Exception("重复插入数据");
            }
            if (state != Hyt.Core.EntityState.Detached)
            {
                throw new Exception("禁止添加代理对象数据");
            }
            return this.Set<T>().Add(model);
        }

        public void Update<T>(T model) where T : class
        {
            var state = GetStatus(model);
            if (state == Hyt.Core.EntityState.Detached)
            {
                this.Set<T>().Attach(model);
                SetStatus(model, Core.EntityState.Modified);               
            }
        }

        public void AddOrUpdate<T>(params T[] models) where T : class
        {
            this.Set<T>().AddOrUpdate(models);
        }

        public void Commit()
        {
            this.SaveChanges();
        }

        public void RollBack()
        {
            
        }
        
        public Core.EntityState GetStatus<T>(T model) where T : class
        {
            System.Data.Entity.EntityState efstate = this.Entry<T>(model).State;
            return (Core.EntityState)((int)efstate);
        }

        public void SetStatus<T>(T model, Core.EntityState state) where T : class
        {
            System.Data.Entity.EntityState efstate = (System.Data.Entity.EntityState)((int)state);
            this.Entry<T>(model).State = efstate;
        }

        public int ExecuteSqlCommand(string sql, params object[] parameters)
        {
            Guard.Check(sql).DontNull("sql");
            return this.Database.ExecuteSqlCommand(sql, parameters);          
        }
        
        public IEnumerator<T> ExecuteSqlQuery<T>(string sql, params object[] parameters)
        {
            Guard.Check(sql).DontNull("sql");
            return this.Database.SqlQuery<T>(sql, parameters).GetEnumerator();
        }

        
    }
}
